'-----------------------------------------------------------------
' PICAXE-08 Toy Traffic Lights with battery saver
'-----------------------------------------------------------------
'
' Periods are:
'   red: 30 seconds without button press, or 3 seconds
'          after button press (up to 30 second maximum)
'   green: 8 seconds
'   amber: 3 seconds
'
' Features automatic power-off if the button isnt pressed for
' 5 minutes. Turns on if button is held down for up to 2 seconds. 
' Turns off if the button is held down for 3 seconds continuously. 
'
' Hardware:
'   pin 0 (leg 7) is GREEN (output)
'   pin 1 (leg 6) is AMBER (output)
'   pin 2 (leg 5) is RED (output)
'   pin 3 (leg 4) is button S1 (input)
'   pin 4 (leg 3) is pull-down (output)
' -----------------------------------------------------------------
symbol red_signal	= %00000100
symbol amber_signal 	= %00000010
symbol green_signal 	= %00000001

symbol red_time	= 5400		'27 seconds
symbol red_min_time 	= 800		'minimum red time of 3 seconds; enforced 
					'even if button pressed during red period
symbol green_time	= 2200		'8 seconds
symbol amber_time	= 800		'3 seconds

symbol off_timeout  	= 800		'3 seconds: time button must be pressed to 
					'switch unit off
symbol idle_timeout 	= 60000	'5 minutes: time button must not be pressed for 
					'unit to auto switch off

symbol delay		= w2		'time parameter/variable for delay loops

symbol pressed		= w3		'1 if button pressed at last poll, 0 if button
					'not pressed at last poll

symbol count		= w4		'number of consecutive polls before unit
					'goes into the off state.

main:
  let dirs = %00010111			' pin 0 is GREEN,  pin 1 is AMBER, pin 2 is RED,
					' pin 3 is button S1, pin 4 is pull-down

'-----------------------------------------------------------------
' The OFF state
'-----------------------------------------------------------------
' Wait until the button is not being pressed. This ensures the
' user has let go of the button after pressing it to turn the unit
' off. It also ensures that the unit does not power itself up if
' the button is held down continuously (eg. if it is placed in the
' toy box in a way that keeps the button pressed).
'-----------------------------------------------------------------
wait_no_press:
  high 4					'disable pull-down
  sleep 2

power_down:
  let pins = %00000000			'enable pull-down
  if pin3 = 1 then wait_no_press

' Wait in low power mode until the button is pressed for at most
' 2 seconds. Do this by sampling the button every 2 seconds with
' low-power sleeps in between until the button is found to be
' pressed.

wait_press:
  high 4					'disable pull-down
  sleep 2
  let pins = %00000000			'enable pull-down
  if pin3 = 0 then wait_press

  let pins = %00010111			'turn all LEDs on to indicate power-up
  sleep 2				'give user time to see the lit LEDs and release
the button

'-----------------------------------------------------------------
' The ON state
'-----------------------------------------------------------------

  let count = 0				'not enough code space to have
					'count=idle_timeout, but this is pretty close.
  let pressed = 0			'precondition for poll subroutine.

red:
  let pins = red_signal			'show RED signal

' Wait for red_time or until button is pressed, whichever comes first.

  let delay = red_time

wait_until_pressed:
  gosub poll

  if delay > 0 AND pressed = 0 then wait_until_pressed

  let delay = red_min_time		'enforce minimum RED time
  gosub wait_period

  let pins = green_signal	show GREEN signal
  let delay = green_time
  gosub wait_period

  let pins = amber_signal		'show AMBER signal
  let delay = amber_time
  gosub wait_period

  goto red				'show RED signal and repeat

' wait_period waits for period determined by value of delay.
' Powers unit down if necessary.
'
' Precondition: delay > 0. 
' Postcondition: delay = 0.

wait_period:
  gosub poll
  if delay > 0 then wait_period
return

' Check the state of the button and power the unit down if the button has not 
' been pressed for idle_timeout consecutive polls, or if the button has been 
' pressed for off_timeout consecutive polls. Also decrements delay.

poll:
  let delay = delay - 1
  pause 1
  if pressed = pin3 then same

' Button has changed status since the last poll

  pressed = 1 - pressed

' Set count to off_timeout if button is now pressed, 
' or idle_timeout if button is now not pressed

  count = off_timeout
  if pressed = 1 then endif
    count = idle_timeout
  endif:

same:
  count = count - 1
  if count = 0 then power_down
return

